package com.yourenbang.lingyun.transaction.web.service.impl.fund;

import com.yourenbang.lingyun.constants.BizCodeMessages;
import com.yourenbang.lingyun.constants.enums.ChannelIdEnums;
import com.yourenbang.lingyun.core.exception.SysBizException;
import com.yourenbang.lingyun.core.exception.UserBizException;
import com.yourenbang.lingyun.core.message.Result;
import com.yourenbang.lingyun.core.util.Generator;
import com.yourenbang.lingyun.core.util.KLog;
import com.yourenbang.lingyun.domain.FundAccount;
import com.yourenbang.lingyun.mapper.FundAccountMapper;
import com.yourenbang.lingyun.transaction.constant.ChangePlanStatus;
import com.yourenbang.lingyun.transaction.constant.JournalStatus;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundAccountJournalMapper;
import com.yourenbang.lingyun.transaction.mapper.TransactionFundAccountMapper;
import com.yourenbang.lingyun.transaction.web.domain.po.FundAccountChangePlan;
import com.yourenbang.lingyun.transaction.web.domain.po.FundAccountJournal;
import com.yourenbang.lingyun.transaction.web.service.fund.AccountSystemService;
import com.yourenbang.lingyun.transaction.web.service.fund.FundAccountChangePlanService;
import com.yourenbang.transaction.api.model.dto.TopupApplyDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;

@Slf4j
@Service
public class AccountSystemServiceImpl implements AccountSystemService {

    @Autowired
    private FundAccountMapper fundAccountMapper;
    @Autowired
    private TransactionFundAccountJournalMapper fundAccountJournalMapper;
    @Autowired
    private FundAccountChangePlanService fundAccountChangePlanService;
    @Autowired
    private TransactionFundAccountMapper transactionFundAccountMapper;

    //发起方不存在
    private static final int INITIATOR_NONE = 0;
    @Override
    @Transactional
    public Result topupApply(TopupApplyDTO dto) {
        /*充值
        系统财务账户到指定系统账户流动
        2journal 插入记录
        3.account更新记录 +-
        4.changeplan更改为changed
        */
//        1change-plan插入记录

        FundAccountChangePlan plan = insertFundAccountChangePlan(dto);
        try {
            //查出两个要变的账户
            FundAccount fundAccount = null;
            if (dto.getFundAccountId() != INITIATOR_NONE) {
             fundAccount = fundAccountMapper.selectByPrimaryKey(dto.getFundAccountId());
                if (fundAccount == null) {
                    return Result.builder().fail(BizCodeMessages.TRANSACTION_ACCOUNT_NONE);
                }
            }

            FundAccount toFundAccount = fundAccountMapper.selectByPrimaryKey(dto.getToFundAccountId());
            if (toFundAccount == null) {
                return Result.builder().fail(BizCodeMessages.TRANSACTION_ACCOUNT_NONE);
            }
//      2journal插入2条记录
//            if (fundAccount != null) {
            if (fundAccount != null) {
                //发起方变动
                //变动之前可提现金额 发起方可提现金额变少
                BigDecimal accountAmountWithdrawableBefore = fundAccount.getAccountAmountWithdrawable();
                //计算变动之后提现金额
                BigDecimal accountAmountWithdrawableAfter = accountAmountWithdrawableBefore.subtract(dto.getChangeAmount());
                if (accountAmountWithdrawableAfter.compareTo(BigDecimal.ZERO) < 0 ) {
                    throw new SysBizException(BizCodeMessages.TRANSACTION_AMOUNT_DEFICIENCY);
                }
                FundAccountJournal journal30 = new FundAccountJournal(plan.getPlatNo());
                journal30.setFundAccountChangePlanId(plan.getFundAccountChangePlanId());
                journal30.setFundAccountId(dto.getFundAccountId());
                journal30.setJournalStatus(JournalStatus.SUCCESS);
                journal30.setJournalChangeValue(dto.getChangeAmount());
                journal30.setAccountJournalAcid(plan.getOrderId());
                journal30.setAccountIncome(BigDecimal.ZERO);
                journal30.setAccountAmountFrozen(BigDecimal.ZERO);
                //可提现变动金额
                journal30.setAccountAmountWithdrawable(dto.getChangeAmount());
                //已提现变动金额
                journal30.setAccountAmountWithdrawed(dto.getChangeAmount());
                journal30.setAccountAmountUndepositable(BigDecimal.ZERO);
                journal30.setAccountIncomeBefore(fundAccount.getAccountIncome());
                journal30.setAccountIncomeAfterwards(fundAccount.getAccountIncome());
                journal30.setAccountAmountFrozenBefore(fundAccount.getAccountAmountFrozen());
                journal30.setAccountAmountFrozenAfterwards(fundAccount.getAccountAmountFrozen());
                journal30.setAccountAmountWithdrawableBefore(accountAmountWithdrawableBefore);
                journal30.setAccountAmountWithdrawableAfterwards(accountAmountWithdrawableAfter);
                //变动之前已提现金额 发起方已提现金额增加
                BigDecimal accountAmountWithdrawedBefore = fundAccount.getAccountAmountWithdrawed();
                journal30.setAccountAmountWithdrawedBefore(accountAmountWithdrawedBefore);
                //计算变动后的已提现金额
                BigDecimal accountAmountWithdrawedAfter = accountAmountWithdrawedBefore.add(dto.getChangeAmount());
                journal30.setAccountAmountWithdrawedAfterwards(accountAmountWithdrawedAfter);
                journal30.setAccountAmountUndepositableBefore(fundAccount.getAccountAmountUndepositable());
                journal30.setAccountAmountUndepositableAfterwards(fundAccount.getAccountAmountUndepositable());
                journal30.setChannelId(dto.getChannelId());
                fundAccountJournalMapper.insert(journal30);
                //3.account更新记录 +-
                //发起方可提现变化,已提现变化
                int i = transactionFundAccountMapper.subtractSysAcc(fundAccount.getAccountType(), dto.getChangeAmount());
                if (i != 1) {
                    throw new SysBizException(BizCodeMessages.TRANSACTION_AMOUNT_DEFICIENCY);
                }
            }



            if (toFundAccount != null) {
                //接收方变动
                FundAccountJournal toJournal30 = new FundAccountJournal(plan.getPlatNo());
                toJournal30.setFundAccountChangePlanId(plan.getFundAccountChangePlanId());
                toJournal30.setFundAccountId(dto.getToFundAccountId());
                //记为成功,异常后改为失败
                toJournal30.setJournalStatus(JournalStatus.SUCCESS);
                toJournal30.setJournalChangeValue(dto.getChangeAmount());
                toJournal30.setAccountJournalAcid(plan.getOrderId());
                //系统通易付押金充值
                toJournal30.setAccountIncome(dto.getChangeAmount());
                toJournal30.setAccountAmountFrozen(BigDecimal.ZERO);
                toJournal30.setAccountAmountWithdrawable(dto.getChangeAmount());
                toJournal30.setAccountAmountWithdrawed(BigDecimal.ZERO);
                toJournal30.setAccountAmountUndepositable(BigDecimal.ZERO);
                //账户收入变动前 接收方收入增加
                BigDecimal toFundAccountAccountIncomeBefore = toFundAccount.getAccountIncome();
                toJournal30.setAccountIncomeBefore(toFundAccountAccountIncomeBefore);
                //计算账户收入变动后
                BigDecimal toFundAccountAccountIncomeAfter = toFundAccountAccountIncomeBefore.add(dto.getChangeAmount());
                toJournal30.setAccountIncomeAfterwards(toFundAccountAccountIncomeAfter);
                toJournal30.setAccountAmountFrozenBefore(toFundAccount.getAccountAmountFrozen());
                toJournal30.setAccountAmountFrozenAfterwards(toFundAccount.getAccountAmountFrozen());
                //变动之前可提现金额 接收方可提现金额增加
                BigDecimal toAccountAmountWithdrawableBefore = toFundAccount.getAccountAmountWithdrawable();
                toJournal30.setAccountAmountWithdrawableBefore(toAccountAmountWithdrawableBefore);
                //计算变动之后可提现金额
                BigDecimal toAccountAmountWithdrawableAfter = toAccountAmountWithdrawableBefore.add(dto.getChangeAmount());
                toJournal30.setAccountAmountWithdrawableAfterwards(toAccountAmountWithdrawableAfter);
                toJournal30.setAccountAmountWithdrawedBefore(toFundAccount.getAccountAmountWithdrawed());
                toJournal30.setAccountAmountWithdrawedAfterwards(toFundAccount.getAccountAmountWithdrawed());
                toJournal30.setAccountAmountUndepositableBefore(toFundAccount.getAccountAmountUndepositable());
                toJournal30.setAccountAmountUndepositableAfterwards(toFundAccount.getAccountAmountUndepositable());
                toJournal30.setChannelId(dto.getChannelId());
                fundAccountJournalMapper.insert(toJournal30);
                //3.account更新记录 +
                //接收方收入变化,可提现变化
                int i = transactionFundAccountMapper.addSysAcc(toFundAccount.getAccountType(), dto.getChangeAmount());
                if (i != 1) {
                    throw new SysBizException(BizCodeMessages.TRANSACTION_AMOUNT_DEFICIENCY);
                }
            }
        } catch (Exception e) {
            //抓到异常,更新plan表为wait to change
            KLog.warn("系统充值错误:{}", e.getMessage());
            plan.setAccountChangePlanStatus(ChangePlanStatus.WAIT_TO_CHANGE);
            fundAccountChangePlanService.updateFundAccountChangePlan(plan);
            throw new UserBizException(BizCodeMessages.TRANSACTION_AMOUNT_DEFICIENCY);
        }
        plan.setAccountChangePlanStatus(ChangePlanStatus.CHANGED);
        fundAccountChangePlanService.updateFundAccountChangePlan(plan);
        return Result.builder().success();
    }

    private FundAccountChangePlan insertFundAccountChangePlan(TopupApplyDTO dto) {
        FundAccountChangePlan plan = new FundAccountChangePlan(dto.getPlatNo());

        plan.setFundAccountId(dto.getFundAccountId());
        plan.setToFundAccountId(dto.getToFundAccountId());
        plan.setOrderId(Generator.nextUUID());
        plan.setAccountChangePlanStatus(ChangePlanStatus.CHANGING);
        plan.setChangeAmount(dto.getChangeAmount());
        plan.setChannelId(ChannelIdEnums.no.getCode());
        fundAccountChangePlanService.insertFundAccountChangePlan(plan);
        return plan;
    }

}
